package ece.edt;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.methods.PostMethod;

import ece.edt.exceptions.CryptageException;
import ece.edt.exceptions.HyperPlanningMethodException;

/**
 * @author laurent.virot
 * Extends PostMethod class to make easier the communication with hyperplanning.
 */
public class HyperPlanningMethod extends PostMethod{
	private String xml;
	private int session;
	private int numeroordrecommunication;

	/**
	 * Contain the 'Fonction' use to communicate with hyperplanning.
	 * Allow to configure it easily.
	 */
	public enum Function{
		/**
		 * First Fonction to use.
		 * Return the session number, the numeroordrecommunication number ans some other information.
		 */
		Parametres(
				"FonctionParametres",
				"<FonctionParametres>" +
					"<Espace G=\"1\"/>" +
				"</FonctionParametres>"),
				
		/**
		 * Use to find user by name.
		 */
		RenvoyerListeDeRessource(
				"FonctionRenvoyerListeDeRessource",
				"<FonctionRenvoyerListeDeRessource>" +
					"<Espace T=\"1\">{ESPACE}</Espace>" +
					"<NumeroRessource T=\"1\">{NUMERO_RESSOURCE}</NumeroRessource>" +
					"<GenreRessource T=\"1\">{GENRE_RESSOURCE}</GenreRessource>" +
					"<GenreRecherche T=\"1\">{GENRE_RECHERCHE}</GenreRecherche>" +
					"<NomRessource T=\"3\">{NOM_RESSOURCE}</NomRessource>" +
					"<AvecPublicationForcee T=\"2\">{AVEC_PUBLICATION_FORCE}</AvecPublicationForcee>" +
				"</FonctionRenvoyerListeDeRessource>"),
					
		/**
		 * Get the course for the selected week and the selected user.
		 */
		EmploiDuTemps(
				"FonctionEmploiDuTemps",
				"<FonctionEmploiDuTemps>" +
					"<GenreRessource T=\"1\">{GENRE_RESSOURCE}</GenreRessource>" +
					"<NumeroRessource T=\"1\">{NUMERO_RESSOURCE}</NumeroRessource>" +
					"<PositionRessource T=\"1\">{POSITION_RESSOURCE}</PositionRessource>" +
					"<NumeroSemaine T=\"1\">{NUMERO_SEMAINE}</NumeroSemaine>" +
					"<GenreAffichageEDT T=\"1\">{GENRE_AFFICHAGE_EDT}</GenreAffichageEDT>" +
				"</FonctionEmploiDuTemps>"),
				
				
		/**
		 * Return the presence domain fot the selected user.
		 */
		DomaineDePresence(
				"FonctionDomaineDePresence",
				"<FonctionDomaineDePresence>" +
					"<NumeroRessource T=\"1\">{NUMERO_RESSOURCE}</NumeroRessource>" +
					"<GenreRessource T=\"1\">{GENRE_RESSOURCE}</GenreRessource>" +
					"<PositionRessource T=\"1\">{POSITION_RESSOURCE}</PositionRessource>" +
				"</FonctionDomaineDePresence>");
		
		/**
		 * Dafault values of Fonction
		 */
		private String[][] defaultValues;
		
		/**
		 * Name of the Fonction
		 */
		private String name;
		
		/**
		 * Body of the Fonction
		 */
		private String str;
		
		/**
		 * Constructor, Set the name, the body and the Fonction.
		 * @param name Name of the Fonction
		 * @param str Body of the Fonction
		 */
		Function(String name, String str){
			this.name = name;
			this.str = str;
			
			// default values for the Fonction
			String[][] values = {
				{"ESPACE", "1"},
				{"NUMERO_RESSOURCE", "0"},
				{"GENRE_RESSOURCE", "2"},
				{"GENRE_RECHERCHE", "1"},
				{"NOM_RESSOURCE", ""},
				{"AVEC_PUBLICATION_FORCE", "FALSE"},
				{"POSITION_RESSOURCE", "0"},
				{"NUMERO_SEMAINE", "0"},
				{"GENRE_AFFICHAGE_EDT", "0"}};
			defaultValues = values;
		}
		
		/**
		 * @return Name of the fonction
		 */
		public String getName(){
			return name;
		}
		
		/**
		 * Set the parameters of a Fonction
		 * @param v Values we want to set.
		 * @return Body with right parameters.
		 */
		public String setParameters(String[][] v){
			String parameters = str;
			// if null, we only use the default values.
			if(v != null){
				// Foreach values, we remplace the parameter in the body of the Fonction
				for(String[] param : v){
					parameters = parameters.replaceAll("\\{"+ param[0] + "\\}", param[1]);
				}
			}
			
			// We parse again but with the default values, for specifying the parameters which have not been set.
			for(String[] param : defaultValues){
				parameters = parameters.replaceAll("\\{"+ param[0] + "\\}", param[1]);
			}

			return parameters;
		}
	}
	
	/**
	 * Initialize a PostMethod and specify the Fonction to use without parameter.
	 * @param f Fonction to use.
	 * @throws HyperPlanningMethodException
	 */
	public HyperPlanningMethod(Function f, String hyperPlanningURL) throws HyperPlanningMethodException{
		this(f, null, hyperPlanningURL);
	}
	
	/**
	 * Initialize a PostMethod and specify the Fonction to use 
	 * @param f Fonction to use.
	 * @param v Parameters.
	 * @throws HyperPlanningMethodException
	 */
	public HyperPlanningMethod(Function f, String[][] v, String hyperPlanningURL) throws HyperPlanningMethodException{
		// Set the url of the method.
		super(hyperPlanningURL + "appelfonction");

		// Define the Post parameters.
		try {
			setParameter("espace", 					"1");
			setParameter("session", 				Integer.toString(HyperPlanning.getSession()));
			setParameter("nomdefonction", 			f.getName());
			setParameter("parametre", 				Cryptage.encrypt(f.setParameters(v), Cryptage.Algo.HEXA));
			setParameter("numeroordrecommunication",Cryptage.encrypt(Integer.toString(HyperPlanning.getNumeroordrecommunication()), Cryptage.Algo.AES));
			setParameter("cryptage", 				"HEXA");
			setParameter("nombrerequetes", 			"1");
		} catch (CryptageException e) {
			throw new HyperPlanningMethodException(e);
		}
	}
	
	/**
	 * Decrypt the response.
	 * @throws HyperPlanningMethodException
	 */
	public void decrypt() throws HyperPlanningMethodException{
		try {
			// Split the reponse in 3 parts.
			Pattern pattern = Pattern.compile("(.*)##(.*)&&(.*)");
			Matcher matcher = pattern.matcher(getResponseBodyAsString());
			
			if(matcher.find()) {
				// First element is the session number.
				this.session = Integer.parseInt(matcher.group(1));
				
				// Second element is the NumeroOrdreCommunication number, encrypted in AES.
				this.numeroordrecommunication = Integer.parseInt(Cryptage.decrypt(matcher.group(2), Cryptage.Algo.AES));
				
				// Third element is the xml reponse, encrypted in HEXA.
				this.xml = Cryptage.decrypt(matcher.group(3), Cryptage.Algo.HEXA);
			}else {
				throw new HyperPlanningMethodException("Bad planning response format.");
			}
		} catch (IOException e) {
			throw new HyperPlanningMethodException(e);
		} catch(NumberFormatException e) {
			throw new HyperPlanningMethodException(e);
		} catch (CryptageException e) {
			throw new HyperPlanningMethodException(e);
		}
	}	
	
	/**
	 * @return NumeroOrdreCommunication number.
	 */
	public int getNumeroOrdreCommunication() {
		return this.numeroordrecommunication;
	}

	/**
	 * @return session number
	 */
	public int getSession() {
		return this.session;
	}
	
	/**
	 * @return xml reponse
	 */
	public String getXML() {
		return this.xml;
	}
}
